#include "cameradecklink.h"

CameraDecklink::CameraDecklink() {
    m_InputFlags = 0;
		
	m_Running = false;
}

CameraDecklink::~CameraDecklink() {
	fprintf(stderr, "CameraDecklink deleted \n");
}

bool CameraDecklink::initializeCamera(IDeckLink *_deckLink) {
    m_PixelFormat = bmdFormat8BitYUV;

    if (_deckLink->QueryInterface(IID_IDeckLinkInput, (void**)&m_DeckLinkInput) != S_OK)
        return false;

    m_Delegate = new DeckLinkCaptureDelegate();
		
    pthread_mutex_init(m_Delegate->sleepMutex, NULL);
    pthread_cond_init(m_Delegate->sleepCond, NULL);
    m_Result = m_DeckLinkInput->SetCallback(m_Delegate);
	
    if (m_Result != S_OK) {
        fprintf(stderr,
                "Setting callback failed - result = %08x\n",
                m_Result);

        return false;
    }

    return true;
}

bool CameraDecklink::startCapture() {
    m_Result = m_DeckLinkInput->EnableVideoInput(   m_SelectedDisplayMode,
                                                    m_PixelFormat,
                                                    m_InputFlags);
    if(m_Result != S_OK) {
        fprintf(stderr,
                "Failed to enable video input. Is another application using the card? - result = %08x\n",
                m_Result);

		m_Running = false;
        return false;
    }

    m_Result = m_DeckLinkInput->StartStreams();
    if(m_Result != S_OK) {
        m_Running = false;
		return false;
    }
	
	m_Running = true;
    return true;
}

// Ilias from CapturePreview example
void CameraDecklink::stopCapture() {
    m_Running = false;
	
	// Stop the capture
    m_DeckLinkInput->StopStreams();

    // Delete capture callback. This also releases.
    m_DeckLinkInput->SetCallback(NULL);

    m_DeckLinkInput->Release();
}

bool CameraDecklink::FrameHasBeenUpdated() {
	return m_Delegate->FrameHasBeenUpdated;
}

void* CameraDecklink::getFrameBytes() {
//	m_Delegate->FrameHasBeenUpdated = false;
//  pthread_mutex_lock(m_Delegate->sleepMutex);
//  pthread_cond_wait(m_Delegate->sleepCond, m_Delegate->sleepMutex);
//  pthread_mutex_unlock(m_Delegate->sleepMutex);
	return m_Delegate->frameBytes;
}

bool CameraDecklink::fetchSelectedDisplayMode(int modeIndex) {
    IDeckLinkDisplayModeIterator*   displayModeIterator;
    int                             displayModeCount;
    bool                            foundDisplayMode;

    // Obtain an IDeckLinkDisplayModeIterator
    // to enumerate the display modes supported on output
    m_Result = m_DeckLinkInput->GetDisplayModeIterator(&displayModeIterator);
    if (m_Result != S_OK) {
        fprintf(stderr, "Could not get display mode iterator - result = %08x\n", m_Result);
        return false;
    }

    displayModeCount = 0;
    while (displayModeIterator->Next(&m_DisplayMode) == S_OK) {

        // Getting the displaymode name and printing it:
        CFStringRef displayModeName;
        m_DisplayMode->GetName(&displayModeName);
 //       QString     dmName      = toQString(displayModeName);
 //       QByteArray  byteArray   = dmName.toUtf8();
 //       const char* cString     = byteArray.constData();
        //QString qString2 = QString::fromUtf8(cString); // example of converting back to QString.
 //       fprintf(stderr, "Display mode name: %s \n", cString);

        if (modeIndex == displayModeCount) {
            BMDDisplayModeSupport result;

            foundDisplayMode = true;

            m_SelectedDisplayMode = m_DisplayMode->GetDisplayMode();

            m_DeckLinkInput->DoesSupportVideoMode(  m_SelectedDisplayMode,
                                                    m_PixelFormat,
                                                    bmdVideoInputFlagDefault,
                                                    &result,
                                                    NULL);

            if (result == bmdDisplayModeNotSupported) {
     //           fprintf(stderr, "The display mode %s is not supported with the selected pixel format\n", cString);
                return false;
            }

            break;
        }
        displayModeCount++;
        m_DisplayMode->Release();
    }

    if (!foundDisplayMode) {
        fprintf(stderr, "Invalid mode %d specified\n", modeIndex);
        return false;
    }

    return true;
}

// --- Delegate:

DeckLinkCaptureDelegate::DeckLinkCaptureDelegate() : m_refCount(0) {
    pthread_mutex_init(&m_mutex, NULL);

 //   lastImage = 0;
    //frameCount = 0;
    stopped = false;
    sleepMutex = new pthread_mutex_t();
    sleepCond = new pthread_cond_t();

	pthread_mutex_init(&framebytesMutex, NULL);

    g_timecodeFormat = 0;

    height = 720;
    width = 1280;
	
	m_DropFrame = false;
	FrameHasBeenUpdated = false;
}

DeckLinkCaptureDelegate::~DeckLinkCaptureDelegate() {
   	pthread_mutex_destroy(&framebytesMutex);
	
	pthread_mutex_destroy(&m_mutex);

    pthread_mutex_destroy(sleepMutex);
    pthread_cond_destroy(sleepCond);
	
	fprintf(stderr, "Delegate done with!\n");
}

ULONG DeckLinkCaptureDelegate::AddRef(void) {
    pthread_mutex_lock(&m_mutex);
    m_refCount++;
    pthread_mutex_unlock(&m_mutex);

    return (ULONG)m_refCount;
}

ULONG DeckLinkCaptureDelegate::Release(void) {
    pthread_mutex_lock(&m_mutex);
    m_refCount--;
    pthread_mutex_unlock(&m_mutex);

    if (m_refCount == 0) {
        delete this;
        return 0;
    }

    return (ULONG)m_refCount;
}

static int frameCount = 0;

HRESULT DeckLinkCaptureDelegate::VideoInputFrameArrived(IDeckLinkVideoInputFrame* videoFrame,
                                                        IDeckLinkAudioInputPacket* audioFrame) {
    unsigned char * pData;
    void*			audioFrameBytes;

    // Handle Video Frame
    if(videoFrame && !stopped) {
        if (videoFrame->GetFlags() & bmdFrameHasNoInputSource) {
    //        fprintf(stderr, "Frame received (#%lu) - No input signal detected\n", frameCount);
        }
        else {
			if(!m_DropFrame) {
				/*
				fprintf(stderr, "Frame received (#%lu) [%s] - %s - rowBytes: %li, h: %li\n",
                    frameCount++,
					"No timecode",
					"Valid Frame",
                    videoFrame->GetRowBytes(), videoFrame->GetHeight());
				*/
				
				pthread_mutex_lock(&framebytesMutex);

				videoFrame->GetBytes(&frameBytes);
							
				FrameHasBeenUpdated = true;
				pthread_mutex_unlock(&framebytesMutex);
			}
			
		//	sleep(1);
			
		//	else {
			//	fprintf(stderr, "Frame Dropped!\n");
		//	}
	
		//	pthread_cond_signal(sleepCond);

			m_DropFrame = !m_DropFrame;
        }
    }

    return S_OK;
}

HRESULT DeckLinkCaptureDelegate::VideoInputFormatChanged(BMDVideoInputFormatChangedEvents events,
                                                         IDeckLinkDisplayMode *mode,
                                                         BMDDetectedVideoInputFormatFlags) {
    return S_OK;
}

